//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/python/parser.h>
#include <errcode.h>
#include <grammar.h>
#include <node.h>
#include <parsetok.h>
#include <token.h>

extern grammar _PyParser_Grammar;

namespace buola { namespace python {

CParserResult::~CParserResult()
{
    if(mNode)
        PyNode_Free((node*)mNode);
}
    
CParserResult parse_line(const std::string &pLine,EParseType pType)
{
    perrdetail lE;
    int lFlags=PyPARSE_DONT_IMPLY_DEDENT|PyPARSE_IGNORE_COOKIE;
    
    int lStart;
    switch(pType)
    {
    case PARSE_FILE:
        lStart=Py_file_input;
        break;
    case PARSE_EXPRESSION:
        lStart=Py_eval_input;
        break;
    case PARSE_LINE:
    default:
        lStart=Py_single_input;
        break;
    }
    
    CParserResult lResult;
    lResult.mIndentLevel=0;
    lResult.mPosition=0;
    lResult.mNode=PyParser_ParseStringFlagsFilename(pLine.c_str(),nullptr,&_PyParser_Grammar,
                                                    lStart,&lE,lFlags);

    if(lResult.mNode)
    {
        lResult.mStatus=PARSE_OK;
        return lResult;
    }
    
    if(lE.error==E_EOFS)
    {
        lResult.mStatus=PARSE_INCOMPLETE;
        return lResult;
    }
    
    //try again with additional \n, to check for incomplete expressions
    perrdetail lE2,lE3;
    node *lNode2=PyParser_ParseStringFlagsFilename((pLine+'\n').c_str(),"<string>",
                                                   &_PyParser_Grammar,lStart,&lE2,lFlags);
    if(lNode2)
    {
        PyNode_Free(lNode2);
        lResult.mStatus=PARSE_INCOMPLETE;
        lResult.mIndentLevel=3;
        return lResult;
    }
    node *lNode3=PyParser_ParseStringFlagsFilename((pLine+"\n\n").c_str(),"<string>",
                                                   &_PyParser_Grammar,lStart,&lE3,lFlags);
    if(lNode3)
    {
        PyNode_Free(lNode3);
    }

    if(lE2.error==E_EOF)
    {
        lResult.mStatus=PARSE_INCOMPLETE;
        lResult.mIndentLevel=3;
        return lResult;
    }
    
    if(lE2.lineno!=lE3.lineno||lE2.offset!=lE3.offset)
    {
        lResult.mStatus=PARSE_INCOMPLETE2;
        return lResult;
    }

    if(lE2.error!=lE3.error)
    {
        lResult.mStatus=PARSE_INCOMPLETE3;
        return lResult;
    }

    lResult.mPosition=lE.offset;
    
    switch(lE.error)
    {
    case E_SYNTAX:
        if(lE.expected==INDENT)
            lResult.mStatus=PARSE_INDENT_EXPECTED;
        else if(lE.token==INDENT)
            lResult.mStatus=PARSE_INDENT_UNEXPECTED;
        else if(lE.token==DEDENT)
            lResult.mStatus=PARSE_UNINDENT_UNEXPECTED;
        else
            lResult.mStatus=PARSE_INVALID_SYNTAX;
        break;
    case E_TOKEN:
        lResult.mStatus=PARSE_INVALID_TOKEN;
        break;
    case E_EOLS:
        lResult.mStatus=PARSE_SINGLE_QUOTE_EOL;
        break;
    case E_TABSPACE:
        lResult.mStatus=PARSE_TABSPACE;
        break;
    case E_OVERFLOW:
        lResult.mStatus=PARSE_OVERFLOW;
        break;
    case E_DEDENT:
        lResult.mStatus=PARSE_WRONG_UNINDENT;
        break;
    case E_TOODEEP:
        lResult.mStatus=PARSE_TOODEEP;
        break;
    case E_LINECONT:
        lResult.mStatus=PARSE_LINECONT;
        break;
    case E_IDENTIFIER:
        lResult.mStatus=PARSE_INVALID_CHARACTER;
        break;
    default:
        lResult.mStatus=PARSE_UNKNOWN_ERROR;
    }

    return lResult;
}

YCode compile_node(const CParserResult &pResult)
{
    if(pResult.mStatus!=PARSE_OK||!pResult.mNode) return nullptr;
    
    return YCode(PyNode_Compile((node*)pResult.mNode,"<string>"));
}

YCode compile_line(const std::string &pLine,EParseType pType)
{
    PyCompilerFlags lFlags;
    lFlags.cf_flags=PyCF_SOURCE_IS_UTF8|PyCF_DONT_IMPLY_DEDENT|PyCF_IGNORE_COOKIE;

    int lStart;
    switch(pType)
    {
    case PARSE_FILE:
        lStart=Py_file_input;
        break;
    case PARSE_EXPRESSION:
        lStart=Py_eval_input;
        break;
    case PARSE_LINE:
    default:
        lStart=Py_single_input;
        break;
    }
    
    return YCode((PyCodeObject*)Py_CompileStringFlags(pLine.c_str(),"<string>",lStart,&lFlags));
}

/*namespace python*/ } /*namespace buola*/ }
